/*
 * License GNU LGPL
 * Copyright (C) 2012 Amrullah <amrullah@panemu.com>.
 */
package com.abc.cheque.ui.table;


import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.control.Control;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.util.Callback;

import org.apache.commons.beanutils.PropertyUtils;

import com.abc.cheque.common.TableCriteria;
import com.abc.cheque.common.TiwulFXUtil;
import com.abc.cheque.control.LookupField;
import com.abc.cheque.control.LookupFieldController;

/**
 *
 * @author Amrullah <amrullah@panemu.com>
 */
public class LookupColumn<S, T> extends BaseColumn<S, T> {

    private LookupField<T> searchInputControl;
    private SearchMenuItemBase<T> searchMenuItem;
    private String lookupPropertyName;
    private LookupFieldController<T> lookupController;

    public LookupColumn(String propertyName, String nestedPropertyName) {
        this(propertyName, nestedPropertyName, 100);
    }

    public LookupColumn(String columnPropertyName, String lookupPropertyName, double prefWidth) {
        super(columnPropertyName, prefWidth);
        this.lookupPropertyName = lookupPropertyName;
        setCellFactory(new Callback<TableColumn<S, T>, TableCell<S, T>>() {
            @Override
            public TableCell<S, T> call(TableColumn<S, T> p) {
                return new LookupTableCell();
            }
        });
    }

    public String getLookupPropertyName() {
        return lookupPropertyName;
    }

    @Override
    MenuItem getSearchMenuItem() {
        return getLookupMenuItem();
    }

    public LookupFieldController<T> getLookupController() {
        return lookupController;
    }

    public void setLookupController(LookupFieldController<T> lookupController) {
        this.lookupController = lookupController;
    }
    private Map<String, T> mapValue = new HashMap<>();

    @Override
    protected T convertFromString_Impl(String stringValue) {
        T result = mapValue.get(stringValue);
        if (result == null && stringValue != null && !stringValue.isEmpty()) {
            List<T> data = lookupController.loadDataForPopup(getLookupPropertyName(), stringValue, TableCriteria.Operator.eq);
            if (data.size() == 1) {
                result = data.get(0);
                mapValue.put(stringValue, result);
            } else {
                result = getLookupController().show(getTableView().getScene().getWindow(), null, getLookupPropertyName(), stringValue);
            }
            
        }
        return result;
    }

    private SearchMenuItemBase<T> getLookupMenuItem() {
        if (searchMenuItem == null) {
            searchInputControl = new LookupField<>();
            searchInputControl.setPropertyName(lookupPropertyName);
            searchInputControl.setShowSuggestionWaitTime(waitTime);
            searchInputControl.setController(lookupController);
            searchMenuItem = new SearchMenuItemBase<T>(this) {
                @Override
                protected Node getInputControl() {
                    return searchInputControl;
                }

                @Override
                protected List<TableCriteria.Operator> getOperators() {
                    List<TableCriteria.Operator> lst = new ArrayList<>();
                    lst.add(TableCriteria.Operator.eq);
                    lst.add(TableCriteria.Operator.ne);
                    lst.add(TableCriteria.Operator.is_null);
                    lst.add(TableCriteria.Operator.is_not_null);
                    return lst;
                }

                @Override
                protected T getValue() {
                    return searchInputControl.getValue();
                }
            };
        }
        return searchMenuItem;
    }
    private int waitTime = TiwulFXUtil.DEFAULT_LOOKUP_SUGGESTION_WAIT_TIMES;

    /**
     * Set wait time in millisecond for showing suggestion list. Set it to -1 to
     * disable suggestion list feature.
     *
     * @param waitTime Default is 500 millisecond
     */
    public void setShowSuggestionWaitTime(int waitTime) {
        this.waitTime = waitTime;
    }

    public int getShowSuggestionWaitTime() {
        return this.waitTime;
    }

    public class LookupTableCell extends BaseCell<S, T> {

        private LookupField<T> lookupField;

        public LookupTableCell() {
            super();
            this.setAlignment(LookupColumn.this.getAlignment());
        }

        @Override
        protected void setValueToEditor(T value) {
            lookupField.setValue(value);
        }

        @Override
        protected T getValueFromEditor() {
            return lookupField.getValue();
        }

        @Override
        protected Control getEditor() {
            if (lookupField == null) {
                lookupField = new LookupField<>();
                lookupField.setShowSuggestionWaitTime(waitTime);
                lookupField.setValue(getItem());
                lookupField.setPropertyName(lookupPropertyName);
                lookupField.setController(getLookupController());
                /**
                 * Disable traversing focus using LEFT, RIGHT, UP and DOWN.
                 */
                lookupField.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
                    @Override
                    public void handle(KeyEvent event) {
                        if ((event.getCode() == KeyCode.LEFT || event.getCode() == KeyCode.RIGHT
                                || event.getCode() == KeyCode.UP
                                || event.getCode() == KeyCode.DOWN)
                                && isEditing()) {
                            event.consume();
                        }
                    }
                });
            }
            return lookupField;
        }

        @Override
        protected String getString(T value) {
            Object string;
            try {
                string = PropertyUtils.getSimpleProperty(value, lookupPropertyName);
            } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) {
                return null;
            } catch (Exception ex) {
                return null;
            }
            return string != null ? string.toString() : null;
        }

        @Override
        protected void attachEnterEscapeEventHandler() {
            /**
             * Use event filter instead on onKeyPressed because Enter and Escape
             * have been consumed by lookupField it self
             */
            lookupField.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
                @Override
                public void handle(KeyEvent t) {
                    if (t.getCode() == KeyCode.ENTER && !t.isControlDown()) {
                        commitEdit(lookupField.getValue());
                    } else if (t.getCode() == KeyCode.ESCAPE) {
                        lookupField.resetDisplayText();
                        cancelEdit();
                        /**
                         * Propagate ESCAPE key press to cell
                         */
                        LookupTableCell.this.fireEvent(t);
                    }
                }
            });

        }

        @Override
        public void commitEdit(T t) {
            super.commitEdit(t);
            forceUpdateRow();
        }

        /**
         * Force update cell values. It is needed in order to update the values
         * of cell that display particular property of currently selected lookup
         * object
         */
        private void forceUpdateRow() {
            ((TableRowControl) getTableRow()).refreshLookupSiblings(getPropertyName());
        }
    }
}
